#include "assert.h"
#include "error.h"
#include "stdio.h"
#include "lock.h"
#include "proc.h"
#include "trap.h"

#include "fs/deffs.h"
#include "fs/inode.h"
#include "fs/vfs.h"
#include "fs/iobuf.h"
#include "fs/device.h"

#define STDIN_BUFSIZE 4096
static char stdin_buf[STDIN_BUFSIZE];
static offset_t p_readPos, p_writePos;  // TODO 何时清零
static waitQueue_t __wait_queue;

void
stdin_write(char c) {
    if (c != '\0') {
        intr_off();
        {
            stdin_buf[p_writePos % STDIN_BUFSIZE] = c;
            if (p_writePos - p_readPos < STDIN_BUFSIZE)
                ++p_writePos;
            if (!waitQueue_isEmpty(&__wait_queue))
                wakeup_queue(&__wait_queue, WT_KBD, 1);
        }
        intr_on();
    }
}

static int
stdin_read(char* buf, size_t len) {
    int ret = 0;
    intr_off();
    {
        for (; ret < len; ++ret, ++p_readPos) {
        try_again:
            if (p_readPos < p_writePos) {
                *buf = stdin_buf[p_readPos % STDIN_BUFSIZE];
                ++buf;
            } else {
                wait_t __wait;
                wait_current_set(&__wait_queue, &__wait, WT_KBD);
                intr_off();

                scheduler();

                intr_on();
                wait_current_del(&__wait_queue, &__wait);
                if (__wait.wakeupFlags == WT_KBD)
                    goto try_again;
                else
                    break;
            }
        }
        if ((p_readPos > STDIN_BUFSIZE) && (p_writePos % STDIN_BUFSIZE > p_readPos % STDIN_BUFSIZE)) {
            p_writePos = p_writePos % STDIN_BUFSIZE;
            p_readPos = p_readPos % STDIN_BUFSIZE;
        }
    }
    intr_on();
    return ret;  //实际读取长度
}

static int
stdin_open(device_t* dev, uint32_t flags) {
    if (flags != O_RDONLY)
        return -E_INVAL;
    return 0;
}

static int
stdin_close(device_t* dev) {
    return 0;
}

static int
stdin_io(device_t* dev, iobuf_t* iob, bool write) {
    if (write)
        return -E_INVAL;
    int ret = stdin_read(iob->io_base, iob->io_resid);
    if (ret > 0)
        iob->io_resid -= ret;
    return ret;
}

static int
stdin_ioctl(device_t* dev, int op, void* data) {
    return -E_INVAL;
}

// init_device : dev_init_##x
void
dev_init_stdin(void) {
    inode_t* node = dev_createInode();
    if (node == NULL)
        panic("stdin: dev_create_node.\n");

    device_t* dev = vop_info(node, device);
    dev->d_blocks = 0;  // char device
    dev->d_blocksize = 1;
    dev->d_open = stdin_open;
    dev->d_close = stdin_close;
    dev->d_io = stdin_io;
    dev->d_ioctl = stdin_ioctl;
    p_readPos = p_writePos = 0;
    waitQueue_init(&__wait_queue);

    int ret = vfs_addDevice("stdin", node, 0);
    if (ret != 0)
        panic("stdin: vfs_add_dev");
}